/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, version 2 as published by the Free Software * Foundation. * * You should have received a copy of the GNU General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * * Copyright 2006 - 2013 Pentaho Corporation. All rights reserved. */ package org.pentaho.platform.engine.core.system.objfac.spring; import org.apache.commons.lang.ClassUtils; import org.pentaho.platform.api.engine.IPluginManager; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.BeanDefinitionDecorator; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * Parses the publish tag of a bean. Exposing the bean to the PentahoSystem as an implementation of the given type. * Beans can be published by a given type, as all implemented interfaces, as all inherited classes, a combination of all * classes and interfaces, or by default as the class of the bean itself. * <p/> * Attributes embedded in the publish tag become available to the PentahoSystem to allow for querying of registered * implementations. An attribute of "priority" is used to determine the order of registered implementations. * <p/> * <pen:bean class="com.foo.Clazz"> <pen:publish as-type="[ INTERFACES | CLASSES | ALL | Classname ]> <pen:attributes> * <pen:attr key="priority" value="50"/> </pen:attributes> </pen:publish> </pen:bean> * <p/> * User: nbaker Date: 3/27/13 */ public class BeanPublishParser implements BeanDefinitionDecorator { private static String ATTR = "as-type"; private IPluginManager pluginManager; private static enum specialPublishTypes { INTERFACES, CLASSES, ALL } @Override public BeanDefinitionHolder decorate( Node node, BeanDefinitionHolder beanDefinitionHolder, ParserContext parserContext ) { String publishType = null; String beanClassName; // If this is a republish of a pen:bean, the class will be a FactoryBean and the actual class returned back the // factory will be stored as an attribute. if ( beanDefinitionHolder.getBeanDefinition().getAttribute( "originalClassName" ) != null ) { beanClassName = beanDefinitionHolder.getBeanDefinition().getAttribute( "originalClassName" ).toString(); } else { beanClassName = beanDefinitionHolder.getBeanDefinition().getBeanClassName(); } if ( node.getAttributes().getNamedItem( ATTR ) != null ) { // publish as type if specified publishType = node.getAttributes().getNamedItem( ATTR ).getNodeValue(); } else { // fallback to publish as itself publishType = beanClassName; } // capture the "ID" of the bean as an attribute so it can be queried for beanDefinitionHolder.getBeanDefinition().setAttribute( "id", beanDefinitionHolder.getBeanName() ); NodeList nodes = node.getChildNodes(); for ( int i = 0; i < nodes.getLength(); i++ ) { Node n = nodes.item( i ); if ( stripNamespace( n.getNodeName() ).equals( Const.ATTRIBUTES ) ) { NodeList attrnodes = n.getChildNodes(); for ( int y = 0; y < attrnodes.getLength(); y++ ) { Node an = attrnodes.item( y ); if ( stripNamespace( an.getNodeName() ).equals( Const.ATTR ) ) { beanDefinitionHolder.getBeanDefinition().setAttribute( an.getAttributes().getNamedItem( Const.KEY ).getNodeValue(), an.getAttributes().getNamedItem( Const.VALUE ).getNodeValue() ); } } } } try { List<Class<?>> classesToPublish = new ArrayList<Class<?>>(); Class<?> clazz = findClass( beanClassName ); if ( specialPublishTypes.INTERFACES.name().equals( publishType ) ) { if ( clazz.isInterface() ) { // publish as self if interface already (re-publish flow) classesToPublish.add( clazz ); } else { classesToPublish.addAll( ClassUtils.getAllInterfaces( clazz ) ); } } else if ( specialPublishTypes.CLASSES.name().equals( publishType ) ) { classesToPublish.addAll( ClassUtils.getAllSuperclasses( clazz ) ); classesToPublish.add( clazz ); } else if ( specialPublishTypes.ALL.name().equals( publishType ) ) { classesToPublish.addAll( ClassUtils.getAllInterfaces( clazz ) ); classesToPublish.addAll( ClassUtils.getAllSuperclasses( clazz ) ); classesToPublish.add( clazz ); } else { classesToPublish.add( getClass().getClassLoader().loadClass( publishType ) ); } String beanFactoryId = null; if ( parserContext.getRegistry().containsBeanDefinition( Const.FACTORY_MARKER ) == false ) { beanFactoryId = UUID.randomUUID().toString(); parserContext.getRegistry().registerBeanDefinition( Const.FACTORY_MARKER, BeanDefinitionBuilder.genericBeanDefinition( Marker.class ).setScope( BeanDefinition.SCOPE_PROTOTYPE ) .addConstructorArgValue( beanFactoryId ).getBeanDefinition() ); } else { beanFactoryId = (String) parserContext.getRegistry().getBeanDefinition( Const.FACTORY_MARKER ) .getConstructorArgumentValues().getArgumentValue( 0, String.class ).getValue(); } for ( Class cls : classesToPublish ) { PublishedBeanRegistry.registerBean( beanDefinitionHolder.getBeanName(), cls, beanFactoryId ); } } catch ( ClassNotFoundException e ) { throw new RuntimeException( "Cannot find class for publish type: " + publishType + " specified on publish of bean id: " + beanDefinitionHolder.getBeanName(), e ); } return beanDefinitionHolder; } private Class<?> findClass( String beanClassName ) throws ClassNotFoundException { // try main classloader // getClass().getClassLoader() Class<?> clazz = loadClassFromClassloader( getClass().getClassLoader(), beanClassName ); if ( clazz != null ) { return clazz; } if (getPluginManager() != null) { for ( String s : getPluginManager().getRegisteredPlugins() ) { clazz = loadClassFromClassloader( getPluginManager().getClassLoader( s ), beanClassName ); if ( clazz != null ) { return clazz; } } } throw new ClassNotFoundException( beanClassName ); } private IPluginManager getPluginManager() { if ( pluginManager == null ) { pluginManager = PentahoSystem.get( IPluginManager.class ); } return pluginManager; } private Class<?> loadClassFromClassloader( ClassLoader loader, String beanClassName ) { try { return loader.loadClass( beanClassName ); } catch ( ClassNotFoundException e ) { //ignored } return null; } private static String stripNamespace( String s ) { if ( s.indexOf( ':' ) > 0 ) { return s.substring( s.indexOf( ':' ) + 1 ); } return s; } }